/*
 *	This file is part of Lounge-Cast.
 *
 *  Author:  	Markus Moormann
 *	E-mail:  	mmoorman@uni-osnabrueck.de
 *	Copyright:	University of Osnabrueck (Zentrum Virtuos)
 * 	Website:	www.uni-osnabrueck.de
 *	Date:		16.02.2011
 *
 *	Lounge-Cast is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *	Lounge-Cast is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Lounge-Cast.  If not, see <http://www.gnu.org/licenses/>.
 */
package webservice.functions;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;

import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.ProduceMime;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;

import org.apache.log4j.Logger;

import webservice.dbConnection.ConnectionHelper;
import webservice.helper.HelperClass;
import webservice.vo.Image;
import webservice.vo.Media;
import webservice.vo.Slide;
import webservice.vo.Text;
import webservice.vo.Video;

/**
 * Webservice class delivering the next media to the player
 * 
 * @author Markus Moormann
 */
@Path("/next")
@ProduceMime("application/xml")
public class NextResource {
	
	/**
	 * a logger to log errors, debug information, etc.
	 */
	private static Logger LOG = Logger.getLogger(NextResource.class);
	
	private static HashMap<String, Iterator<Media>> mediaLists = new HashMap<String, Iterator<Media>>();
	private static HashMap<String, Long> last_updates = new HashMap<String, Long>();
	
	/**
	 * get the next MediaItem for client
	 * called as http://example.org/webservice/uploadedFiles/next?playerId=&apiKey="
	 * 
	 * @param apiKey the apiKey entered in client
	 * @param playerId the id of the client
	 * @return a <code>Media</code> transformed into a XML
	 */
	@GET
	public Media next(@QueryParam("apiKey") @DefaultValue("") String apiKey,
						@QueryParam("playerId") @DefaultValue("") String playerId) {
		Media media = new Media();
		//is apiKey correct?
		if(!apiKey.equals(HelperClass.getProperty("apiKey"))) {
			LOG.error("player '" + playerId + "' tried to access Webservice with incorrect apiKey");
			throw new WebApplicationException(Response.Status.FORBIDDEN);
		}
		//is there a playerId given?
		if(playerId.equals("")) {
			throw new WebApplicationException(Response.Status.BAD_REQUEST); 
		}
		Connection connection = null;
		Statement statement = null;
		try {
			connection = ConnectionHelper.getConnection();
			statement = connection.createStatement();
			Long last_saved_update = last_updates.get(playerId);
			Long last_real_update = getLastUpdate(playerId);
			Iterator<Media> nextMediaIterator = mediaLists.get(playerId);
			if(nextMediaIterator == null || !nextMediaIterator.hasNext() || !last_real_update.equals(last_saved_update))
			{
				LOG.debug("loading media to cache");
				String selectNext = "SELECT   " +
										"DISTINCT(m.media_id)," +
										"m.type                                    , " +
										"m.title                                   , " +
										"UNIX_TIMESTAMP(m.start_date) AS start_date, " +
										"UNIX_TIMESTAMP(m.valid_date) AS valid_date, " +
										"m.newsticker                              , " +
										"m.duration                                , " +
										"m.reference                               , " +
										"m.feed_item_uri						   , " +
										"m.hide_title							   , " +
										"m.title_color  						   , " +
										"position " +
									"FROM " +
										"(SELECT " +
											"mp1.playlist_id AS play1, " +
											"mp2.playlist_id AS play2, " +
											"mp3.playlist_id AS play3 " +
										"FROM " +
											"`media_playlist` mp1 " +
										"LEFT JOIN " +
											"media_playlist mp2 " +
												"ON mp1.inheritance_from = mp2.playlist_id " +
										"LEFT JOIN " +
											"media_playlist mp3 " +
												"ON mp2.inheritance_from = mp3.playlist_id " +
										"WHERE " +
											"mp1.playlist_id IN " +
												"(SELECT " +
													"playlist_id " +
												"FROM " +
													"playlist_player " +
												"WHERE " +
													"player_ID = '" + playerId + "' " +
												") " +
										") t," +
										"media_playlist_items mpi, " +
										"media m, " +
										"player p, " +
										"playlist_player pp " +
									"WHERE " +
										"(mpi.playlist_id = play1 OR " +
										" mpi.playlist_id = play2 OR " +
										" mpi.playlist_id = play3 " +
										") AND " +
										//"position > p.current_position AND " +
										"p.player_id = '" + playerId + "' AND " +
										"m.media_id  = mpi.media_id AND " +
										"p.player_id = pp.player_id AND " +
										"m.valid_date >= NOW() AND " +
										"m.start_date <= NOW() AND " +
										"m.approved = 'true' " +
									"ORDER BY " +
										"position ASC";
				ResultSet rs = statement.executeQuery(selectNext);
				LinkedList<Media> mediaList = new LinkedList<Media>();
				Media mediaTmp;
				while(rs.next())
				{
					mediaTmp = new Media(rs.getString("media_id"), 
										 rs.getString("type"), 
										 rs.getString("title"), 
										 rs.getLong("start_date"),
										 rs.getLong("valid_date"),
										 rs.getBoolean("newsticker"), 
										 rs.getLong("duration"), 
										 rs.getString("reference"));
					mediaTmp.feed_item_uri = rs.getString("feed_item_uri");
					mediaTmp.hide_title = rs.getString("hide_title");
					mediaTmp.title_color = rs.getInt("title_color");
					mediaList.add(mediaTmp);
				}
				mediaLists.put(playerId, mediaList.listIterator());
				last_updates.put(playerId, last_real_update);
				nextMediaIterator = mediaLists.get(playerId);
				LOG.debug(mediaList.size());
			}
			media = nextMediaIterator.next();
			
			insertCounter(playerId, media.media_id);
			ConnectionHelper.close(statement);
			//according to media type get correct MediaObject
			if(media.type.equals("video")) {
				return getVideoObject(media);
			} else if(media.type.equals("text")) {
				return getTextObject(media);
			} else if(media.type.equals("slide")) {
				return getSlideObject(media);
			} else if(media.type.equals("image")) {
				return getImageObject(media);
			}
		} catch (SQLException e) {
			LOG.error(e);
			throw new WebApplicationException(Response.Status.CONFLICT);
		} finally {
			ConnectionHelper.close(connection);
			ConnectionHelper.close(statement);
		}
		return media;
	}
	
	private Long getLastUpdate(String playerId) {
		Connection connection = null;
		Statement statement = null;
			
		try {
			connection = ConnectionHelper.getConnection();
			statement = connection.createStatement();
			
			String selectUpdate = 	"SELECT " +
										"UNIX_TIMESTAMP(last_update) AS last_update " +
									"FROM " +
										"player " +
									"WHERE " +
										"player_id = '" + playerId + "'";
			ResultSet rs = statement.executeQuery(selectUpdate);
			if(rs.next())
			{
				return rs.getLong("last_update");
			}
		} catch (SQLException e) {
			LOG.debug("error when getting last update");
		} finally
		{
			ConnectionHelper.close(statement);
			ConnectionHelper.close(connection);
		}
		return 0L;
	}

	/**
	 * insert an item into the database, that media item was called
	 * 
	 * @param playerId the id of the calling client
	 * @param mediaId the id of the called media
	 */
	private void insertCounter(String playerId, String mediaId) {
		String insertCounter = "INSERT INTO " +
									"media_counter" +
										"(" +
											"media_id," +
											"player_id" +
										") VALUES" +
										"(" +
											"'" + mediaId + "'," +
											"'" + playerId + "'" +
										")";
		Connection connection = null;
		Statement statement = null;
		try {
			connection = ConnectionHelper.getConnection();
			statement = connection.createStatement();
			
			statement.execute(insertCounter);
		} catch (SQLException e) {
			LOG.error(e);
			throw new WebApplicationException(Response.Status.CONFLICT);
		} finally {
			ConnectionHelper.close(connection);
			ConnectionHelper.close(statement);
		}
	}
	
	/**
	 * get the VideoObject to given media
	 * 
	 * @param media the MediaObject
	 * @return a <code>Video</code> Object 
	 */
	private Video getVideoObject(Media media) {
		Connection connection = null;
		Statement statement = null;
		try {
			connection = ConnectionHelper.getConnection();
			statement = connection.createStatement();
			String selectVideo = "SELECT " +
								 	"url," +
								 	"subtitles," +
								 	"volume," +
								 	"in_point," +
								 	"out_point," +
								 	"aspect_ratio " +
								 "FROM " +
								 	"video " +
								 "WHERE " +
								 	"media_id = '" + media.media_id + "'";
			ResultSet rs = statement.executeQuery(selectVideo);
			if(rs.next()) {
			return new Video(media,
						 rs.getString("url"),
						 rs.getString("subtitles"),
						 rs.getDouble("volume"),
						 rs.getDouble("in_point"),
						 rs.getDouble("out_point"),
						 rs.getString("aspect_ratio"));
			}
		} catch (SQLException e) {
			LOG.error(e);
			throw new WebApplicationException(Response.Status.CONFLICT);
		} finally {
			ConnectionHelper.close(connection);
			ConnectionHelper.close(statement);
		}
		return new Video();
	}
	
	/**
	 * get the SlideObject to given media
	 * 
	 * @param media the MediaObject
	 * @return a <code>Slide</code> Object 
	 */
	private Slide getSlideObject(Media media) {
		Connection connection = null;
		Statement statement = null;
		try {
			connection = ConnectionHelper.getConnection();
			statement = connection.createStatement();
			String selectSlide = "SELECT " +
							"url," +
							"aspect_ratio " +
						 "FROM " +
						 	"slide " +
						 "WHERE " +
						 	"media_id = '" + media.media_id + "'";
			ResultSet rs = statement.executeQuery(selectSlide);
			if(rs.next()) {
				return new Slide(media, rs.getString("url"), rs.getString("aspect_ratio"));
			}
		} catch (SQLException e) {
			LOG.error(e);
			throw new WebApplicationException(Response.Status.CONFLICT);
		} finally {
			ConnectionHelper.close(connection);
			ConnectionHelper.close(statement);
		}
		return new Slide();
	}
	
	
	/**
	 * get the TextObject to given media
	 * 
	 * @param media the MediaObject
	 * @return a <code>Text</code> Object 
	 */
	private Text getTextObject(Media media) {
		Connection connection = null;
		Statement statement = null;
		try {
			connection = ConnectionHelper.getConnection();
			statement = connection.createStatement();
			String selectText = "SELECT " +
									"t.text," +
									"t.formated_text," +
									"t.background_color," +
									"t.skin," +
									"t.background_image," +
									"i.url as image," +
									"i.image_source," +
									"i.width," +
									"i.height," +
									"i.aspect_ratio as image_aspect_ratio," +
									"i.media_id as image_media_id," +
									"v.url as video," +
									"v.media_id as video_media_id," +
									"v.aspect_ratio as video_aspect_ratio," +
									"t.image_orientation," +
									"t.margin_bottom," +
									"t.margin_left," +
									"t.margin_top," +
									"t.margin_right " +
								"FROM " +
									"text t " +
								"LEFT JOIN " +
									"image i " +
										"ON(t.image = i.media_id) " +
								"LEFT JOIN " +
									"video v " +
										"ON(t.video = v.media_id)" +
								"WHERE " +
									"t.media_id = '" + media.media_id + "'";
			ResultSet rs = statement.executeQuery(selectText);
			if(rs.next()) {
				Text text = new Text(media,
						rs.getString("text"),
						rs.getString("formated_text"),
						rs.getInt("background_color"),
						rs.getString("skin"),
						rs.getString("video"),
						rs.getString("video_aspect_ratio"),
						rs.getString("image"),
						rs.getString("image_source"),
						rs.getString("image_orientation"),
						rs.getInt("width"),
						rs.getInt("height"),
						rs.getFloat("image_aspect_ratio"),
						rs.getString("background_image"),
						rs.getInt("margin_bottom"),
						rs.getInt("margin_left"),
						rs.getInt("margin_top"),
						rs.getInt("margin_right"));
				String sql = "SELECT feed_item_pubDate FROM feed_item WHERE feed_item_uri = '" + media.feed_item_uri + "'";
				rs = statement.executeQuery(sql);
				if(rs.next()) {
					text.publish_date = HelperClass.format(rs.getDate("feed_item_pubDate"), "dd.MM.yyyy");
				}
				return text;
			}
		} catch (SQLException e) {
			LOG.error(e,e);
			throw new WebApplicationException(Response.Status.CONFLICT);
		} finally {
			ConnectionHelper.close(connection);
			ConnectionHelper.close(statement);
		}
		return new Text();
	}
	
	/**
	 * get the ImageObject to given media
	 * 
	 * @param media the MediaObject
	 * @return a <code>Image</code> Object 
	 */
	private Image getImageObject(Media media) {
		Connection connection = null;
		Statement statement = null;
		try {
			connection = ConnectionHelper.getConnection();
			statement = connection.createStatement();
			String selectText = "SELECT " +
									"url," +
									"image_source," +
									"width," +
									"height," +
									"aspect_ratio " +
								"FROM " +
									"image " +
								"WHERE " +
									"media_id = '" + media.media_id + "'";
			ResultSet rs = statement.executeQuery(selectText);
			if(rs.next()) {
			return new Image(media,
							 rs.getString("url"),
							 rs.getString("image_source"),
							 rs.getInt("width"),
							 rs.getInt("height"),
							 rs.getFloat("aspect_ratio"));
			}
		} catch (SQLException e) {
			LOG.error(e);
			throw new WebApplicationException(Response.Status.CONFLICT);
		} finally {
			ConnectionHelper.close(connection);
			ConnectionHelper.close(statement);
		}
		return new Image();
	}

	
	/**
	 * update current position of the player
	 * 
	 * @param position the new position of the player
	 * @param playerId the id of the player
	 */
	@SuppressWarnings("unused")
	private void updatePosition(float position, String playerId) {
		String updatePosition =	"UPDATE " +
									"player " +
								"SET " +
									"current_position = " + position + " " +
								"WHERE " +
									"player_id = '" + playerId + "'";
		Connection connection = null;
		Statement statement = null;
		try {
			connection = ConnectionHelper.getConnection();
			statement = connection.createStatement();
			
			statement.executeUpdate(updatePosition);
		} catch (SQLException e) {
			LOG.error(e);
			throw new WebApplicationException(Response.Status.CONFLICT);
		} finally {
			ConnectionHelper.close(connection);
			ConnectionHelper.close(statement);
		}
	}

}
